package com.jxltech.krpano.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;
import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.extra.ssh.Sftp;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jxltech.krpano.api.config.Global;
import com.jxltech.krpano.api.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.sql.Struct;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

/**
 * Created by feng on 2020/8/29 0029
 */
@Service
@Slf4j
public class ApiServiceImpl implements ApiService {

	private static final String LOGO_CONTENT = "\n<div style=\"position:absolute;z-index:99999; left: 5px; width: 35%;\"><a href=\"javascript:history.go(-1);\"><img src=\"logo.png\" style=\"height: 100%;width: 100%;\" style=alt=\"3D能博会\" /></a></div>\n";
	private static final String LOGO_RETURN_CONTENT = "\n<div style=\"position:absolute;z-index:99999; left: 12px; width: 90%; margin-top: 10%;\"><a href=\"javascript:history.go(-1);\"><img src=\"return.png\" style=\"height:10%;width:10%;\" style=alt=\"返回\" /></a></div>\n";

	@Resource
	private Global global;

	private String devRelativeStaticPath = Constants.DEV_REALTIVE_STATIC_PATH;

	private String prodRelativeStaticPath = Constants.PROD_REALTIVE_STATIC_PATH;

	private String envRelativeStaticPath = Constants.ENV_REALTIVE_STATIC_PATH;

	private Map<String, List<Pair<String, String>>> partitionMap = Maps.newHashMap();

	private long lastPartitionReloadTime = 0;
	private long lastPartitionBackTime = 0;

	/**
	 * 每5秒检查一次 partition.xlsx 文件
	 */
	@Scheduled(initialDelay = 3000, fixedRate = 5000)
	public void reloadPartition() {
		if (lastPartitionReloadTime == FileUtil.file(global.getPartitionFile()).lastModified()) return;
		log.info("load resource start ...");
		Map<String, List<Pair<String, String>>> newPartitionMap = Maps.newHashMap();
		ExcelReader reader = ExcelUtil.getReader(FileUtil.file(global.getPartitionFile()));
		for (int i = 1; i < reader.getRowCount(); i++) {
			try {
				if (reader.getCell(1, i) == null || reader.getCell(3, i) == null || reader.getCell(4, i) == null || reader.getCell(5, i) == null)
					continue;
				String areaId = reader.getCell(1, i).getStringCellValue();
				String name = reader.getCell(3, i).getStringCellValue();
				String domain = reader.getCell(4, i).getStringCellValue();
				String room = reader.getCell(5, i).getStringCellValue();
				if (StringUtils.isEmpty(areaId) || StringUtils.isEmpty(name) || StringUtils.isEmpty(domain)) continue;
				newPartitionMap.computeIfAbsent(areaId, k -> {
					initPartitionDir(areaId);
					return Lists.newArrayList();
				});
				List<Pair<String, String>> pairList = newPartitionMap.get(areaId);
				String url = String.format(Constants.PARTITION_VIEW_URL, areaId, room, domain);
				pairList.add(new Pair<>(name, url));

				String customerFilePath = global.getPartitionDir()
						+ File.separator + areaId
						+ File.separator + room
						+ File.separator + domain;
				if (!FileUtil.exist(customerFilePath)) continue;

				//自动解压
				if (global.isAutoUnzip()) {
					File zipFile = FileUtil.file(customerFilePath + ".zip");
					File srcFile = FileUtil.file(customerFilePath);
					if (zipFile.exists() && !srcFile.exists()) {
						ZipUtil.unzip(customerFilePath + ".zip", zipFile.getParent());
					}
					FileUtil.del(customerFilePath + ".zip");
				}

				// 自动设置logo
				if (StrUtil.isNotBlank(global.getLogo()) && FileUtil.exist(global.getLogo())
						&& FileUtil.exist(customerFilePath + File.separator + "index.html")
						&& !FileUtil.exist(customerFilePath + File.separator + "index.html_bak")
						&& !FileUtil.exist(customerFilePath + File.separator + "logo.png")) {
					// 复制logo.png
					FileUtil.copy(global.getLogo(), customerFilePath + File.separator + "logo.png", true);
					//bak index.html
					FileUtil.copy(customerFilePath + File.separator + "index.html", customerFilePath + File.separator + "index.html_bak", true);
					//修改内容
					String content = FileUtil.readString(customerFilePath + File.separator + "index.html", CharsetUtil.UTF_8);
					int begin = content.indexOf("<noscript>");
					String resContent = content.substring(0, begin) + LOGO_CONTENT + content.substring(begin);
					FileUtil.writeString(resContent, customerFilePath + File.separator + "index.html", CharsetUtil.UTF_8);
				}

				// 自动设置return logo
				if (StrUtil.isNotBlank(global.getReturnLogo())
						&& FileUtil.exist(customerFilePath + File.separator + "index.html_bak")
						&& !FileUtil.exist(customerFilePath + File.separator + "index.html_bak2")) {
					// 复制return.png
					FileUtil.copy(global.getReturnLogo(), customerFilePath + File.separator + "return.png", true);
					//bak index.html_bak
					FileUtil.copy(customerFilePath + File.separator + "index.html_bak", customerFilePath + File.separator + "index.html_bak2", true);

					//修改内容
					String content = FileUtil.readString(customerFilePath + File.separator + "index.html", CharsetUtil.UTF_8);
					//替换原logo链接
					content = content.replace("javascript:history.go(-1);", "https://www.cntyenergyexpo.com/");
					int begin = content.indexOf("<noscript>");
					String resContent = content.substring(0, begin) + LOGO_RETURN_CONTENT + content.substring(begin);
					FileUtil.writeString(resContent, customerFilePath + File.separator + "index.html", CharsetUtil.UTF_8);

				}

				// 自动设置return 替换为 https://3d.cntyenergyexpo.com/vtour/tour.html
				if (StrUtil.isNotBlank(global.getReturnLogo())
						&& FileUtil.exist(customerFilePath + File.separator + "index.html_bak")
						&& FileUtil.exist(customerFilePath + File.separator + "index.html_bak2")
						&& !FileUtil.exist(customerFilePath + File.separator + "index.html_bak3")) {
					//bak index.html_bak
					FileUtil.copy(customerFilePath + File.separator + "index.html_bak2", customerFilePath + File.separator + "index.html_bak3", true);
					//修改内容
					String content = FileUtil.readString(customerFilePath + File.separator + "index.html", CharsetUtil.UTF_8);
					//替换原logo链接
					content = content.replace("javascript:history.go(-1);", "https://3d.cntyenergyexpo.com/vtour/tour.html");
					FileUtil.writeString(content, customerFilePath + File.separator + "index.html", CharsetUtil.UTF_8);
				}

				//修改title
				if (FileUtil.exist(customerFilePath + File.separator + "index.html")) {
					String content = FileUtil.readString(customerFilePath + File.separator + "index.html", CharsetUtil.UTF_8);
					if (content.contains("<title>undefined</title>")) {
						String resContent = content.replace("<title>undefined</title>", "<title>" + name + "</title>");
						FileUtil.writeString(resContent, customerFilePath + File.separator + "index.html", CharsetUtil.UTF_8);
					}
				}

				//删除 {customerFilePath}/popwindow/3d2/src/music/music.mp3
				//删除 {customerFilePath}/popwindow/3d/src/music/music.mp3
				FileUtil.del(customerFilePath + "/popwindow/3d/src/music.mp3");
				FileUtil.del(customerFilePath + "/popwindow/3d2/src/music.mp3");

			} catch (Exception e) {
				log.error(e.getMessage(), e);
			}

		}
		log.info("load resource success");
		partitionMap = newPartitionMap;
		reader.close();
		lastPartitionReloadTime = FileUtil.file(global.getPartitionFile()).lastModified();

	}

	/**
	 * 每天 2 点备份一次
	 */
	@Scheduled(cron = "0 0 2 * * ?")
	public void backupUserData() {
		if (lastPartitionBackTime == FileUtil.file(global.getPartitionFile()).lastModified()) return;
		if (StringUtils.isEmpty(global.getBackupServer()) || StringUtils.isEmpty(global.getBackupDir())
				|| StringUtils.isEmpty(global.getBackupUser()) || StringUtils.isEmpty(global.getBackupPass()))
			return;
		log.info("backup user data start ...");
		String ftpDir = global.getBackupDir();
		String lastDir = ftpDir + "/last";
		String currentDir = ftpDir + "/current";

		Sftp sftp = null;
		try {
			JSch.setConfig("PreferredAuthentications", "publickey,password,keyboard-interactive");
			//SFTP登录
			sftp = new Sftp(global.getBackupServer(),
					global.getBackupPort(),
					global.getBackupUser(),
					global.getBackupPass());
			sftp.delDir(lastDir);
			if (sftp.exist(currentDir))
				sftp.getClient().rename(currentDir, lastDir);
			if (!sftp.exist(currentDir))
				sftp.mkdir(currentDir);
			File partitionDir = FileUtil.file(global.getPartitionDir());
			//展区
			String[] areaDirNames = partitionDir.list();
			assert areaDirNames != null;
			for (String areaDirName : areaDirNames) {
				File areaDir = FileUtil.file(partitionDir.getAbsolutePath() + File.separator + areaDirName);
				if (!areaDir.isDirectory()) continue;

				String ftpAreaPath = currentDir + "/" + areaDirName;
				if (!sftp.exist(ftpAreaPath))
					sftp.mkdir(ftpAreaPath);

				//大中小 房间
				File[] roomDirs = areaDir.listFiles();
				assert roomDirs != null;
				for (File roomDir : roomDirs) {
					// 厂商全景数据
					File[] customerDirs = roomDir.listFiles();
					if (customerDirs == null) continue;

					String roomAreaPath = ftpAreaPath + "/" + roomDir.getName();
					if (!sftp.exist(roomAreaPath))
						sftp.mkdir(roomAreaPath);

					for (File customerDir : customerDirs) {
						ZipUtil.zip(FileUtil.file(customerDir.getAbsolutePath() + ".zip"), CharsetUtil.defaultCharset(), true, customerDir);
						sftp.upload(roomAreaPath, FileUtil.file(customerDir.getAbsolutePath() + ".zip"));
						FileUtil.del(customerDir.getAbsolutePath() + ".zip");
						log.info("{} backup done", customerDir.getAbsolutePath());
					}
				}
			}

			log.info("backup user data success");
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			if (sftp != null) {
				sftp.close();
			}
			lastPartitionBackTime = FileUtil.file(global.getPartitionFile()).lastModified();
		}
	}

	private void initPartitionDir(String areaId) {
		String path = global.getPartitionDir() + File.separator + areaId;
		if (!FileUtil.exist(path)) {
			FileUtil.mkdir(path);
			FileUtil.mkdir(path + File.separator + "big");
			FileUtil.mkdir(path + File.separator + "normal");
			FileUtil.mkdir(path + File.separator + "small");
		}
	}

	@Override
	public void saveUploadFile(String name, String type, MultipartFile file) throws Exception {
		if (!file.isEmpty()) {
			InputStream fileInputStream = file.getInputStream();
			String fileName = file.getOriginalFilename();
			String extension = FilenameUtils.getExtension(fileName);
			String baseName = FilenameUtils.getBaseName(fileName);

			if (type.equalsIgnoreCase(Constants.FILE_TYPE_MUSIC)) {
				String musicDir = global.getDesignPath() + "/" + name + "/music";
				if (!FileUtil.exist(musicDir)) FileUtil.mkdir(musicDir);
				FileUtil.writeFromStream(fileInputStream, musicDir + "/" + fileName);
			} else if (type.equalsIgnoreCase(Constants.FILE_TYPE_VIDEO)) {
				String videoDir = global.getDesignPath() + "/" + name + "/video";
				if (!FileUtil.exist(videoDir)) FileUtil.mkdir(videoDir);
				FileUtil.writeFromStream(fileInputStream, videoDir + "/" + fileName);
			} else if (type.equalsIgnoreCase(Constants.FILE_TYPE_PIC)) {
				String picDir = global.getDesignPath() + "/" + name + "/pic";
				if (!FileUtil.exist(picDir)) FileUtil.mkdir(picDir);
				FileUtil.writeFromStream(fileInputStream, picDir + "/" + fileName);
			} else {
				throw new Exception("不支持的文件格式");
			}
		}
	}

	@Override
	public void deleteFile(String name, String type, String fileName) {
		if (type.equalsIgnoreCase(Constants.FILE_TYPE_MUSIC)) {
			String musicFile = global.getDesignPath() + "/" + name + "/music/" + fileName;
			FileUtil.del(musicFile);
		} else if (type.equalsIgnoreCase(Constants.FILE_TYPE_VIDEO)) {
			String videoFile = global.getDesignPath() + "/" + name + "/video/" + fileName;
			FileUtil.del(videoFile);
		} else if (type.equalsIgnoreCase(Constants.FILE_TYPE_PIC)) {
			String picFile = global.getDesignPath() + "/" + name + "/pic/" + fileName;
			FileUtil.del(picFile);
		} else {
			throw new RuntimeException("不支持的文件格式");
		}
	}

	@Override
	public List<Pair<String, String>> getPartitions(String areaId) {
		return partitionMap.get(areaId);
	}

	@Override
	public String[] list(String name, String type) {
		if (type.equalsIgnoreCase(Constants.FILE_TYPE_MUSIC)) {
			String musicDir = global.getDesignPath() + "/" + name + "/music";
			if (!FileUtil.exist(musicDir)) FileUtil.mkdir(musicDir);
			return FileUtil.file(musicDir).list();
		} else if (type.equalsIgnoreCase(Constants.FILE_TYPE_VIDEO)) {
			String videoDir = global.getDesignPath() + "/" + name + "/video";
			if (!FileUtil.exist(videoDir)) FileUtil.mkdir(videoDir);
			return FileUtil.file(videoDir).list();
		} else if (type.equalsIgnoreCase(Constants.FILE_TYPE_PIC)) {
			String picDir = global.getDesignPath() + "/" + name + "/pic";
			if (!FileUtil.exist(picDir)) FileUtil.mkdir(picDir);
			return FileUtil.file(picDir).list();
		} else {
			throw new RuntimeException("不支持的文件格式");
		}
	}

	@Override
	public void saveForm(JSONObject jsonObject) {
		String name = jsonObject.getString("name");
		String designNameDir = global.getDesignPath() + "/" + name;
		if (!FileUtil.exist(designNameDir)) FileUtil.mkdir(designNameDir);
		FileUtil.writeUtf8String(jsonObject.toString(), designNameDir + "/" + name + ".json");
	}

	private void sendMail(String name) {

		MailAccount account = new MailAccount();
		account.setHost("smtp.ym.163.com");
		account.setPort(994);
		account.setFrom("fengj@jxltech.com");
		account.setUser("fengj@jxltech.com");
		account.setPass("fengj123");
		account.setSslEnable(true);

		MailUtil.send(account,
				CollUtil.newArrayList(global.getDesignMail().split(",")),
				"全景图申请", "申请厂商：" + name, false);

	}

	@Override
	public void submit(JSONObject jsonObject) {
		saveForm(jsonObject);
		sendMail(jsonObject.getString("name"));
	}

	@Override
	public void reset(String name) {
		ExcelWriter writer = ExcelUtil.getWriter(FileUtil.file(global.getPartitionFile()));
		for (int i = 1; i < writer.getRowCount(); i++) {
			try {
				if (writer.getCell(1, i) == null || writer.getCell(3, i) == null || writer.getCell(4, i) == null || writer.getCell(5, i) == null)
					continue;
				String eName = writer.getCell(3, i).getStringCellValue();
				if (eName.equalsIgnoreCase(name)) {
					writer.getCell(3, i).setBlank();
					writer.getCell(4, i).setCellValue("");
					writer.getCell(5, i).setCellValue("");
				}

			} catch (Exception e) {
				log.error(e.getMessage(), e);
			}
		}
		writer.close();
		FileUtil.del(global.getDesignPath() + "/" + name);
	}

	@Override
	public JSONObject getFormInfo(String name) {
		String formPath = global.getDesignPath() + "/" + name + "/" + name + ".json";
		if (FileUtil.exist(formPath)) {
			String jsonStr = FileUtil.readUtf8String(formPath);
			JSONObject jsonObject = JSON.parseObject(jsonStr);
			String areaId = jsonObject.getString("areaId");
			List<Pair<String, String>> pairList = getPartitions(areaId);
			if (CollectionUtil.isNotEmpty(pairList)) {
				pairList.forEach(item -> {
					if (item.getKey().trim().equalsIgnoreCase(jsonObject.getString("name"))) {
						jsonObject.put("url", item.getValue());
						jsonObject.put("status", 3);
					}
				});
			}
			return jsonObject;
		}
		return null;
	}

	@Override
	public List<Pair<String, String>> getTemplates() {
		List<Pair<String, String>> pairList = Lists.newArrayList();
		File templateDir = FileUtil.file(global.getTemplatePath());
		String[] templates = templateDir.list();
		if (templates != null) {
			for (String template : templates) {
				pairList.add(new Pair<>(template, String.format(Constants.TEMPLATE_VIEW_URL, template)));
			}
		}
		return pairList;
	}

}
